/*
 * Title:        CloudSim Toolkit
 * Description:  CloudSim (Cloud Simulation) Toolkit for Modeling and Simulation of Clouds
 * Licence:      GPL - http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (c) 2009-2012, The University of Melbourne, Australia
 */

package cloudsim.network;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.StringTokenizer;

/**
 * A file reader for the special BRITE-format. A BRITE file is structured as
 * follows:<br/>
 * <ul>
 * <li>Node-section: NodeID, xpos, ypos, indegree, outdegree, ASid, type(router/AS)
 * <li>Edge-section: EdgeID, fromNode, toNode, euclideanLength, linkDelay, linkBandwith, AS_from, AS_to,
 * type
 * </ul>
 *
 * @author Thomas Hohnstein
 * @since CloudSim Toolkit 1.0
 */
public class GraphReaderBrite implements GraphReaderIF {

    private static final int PARSE_NOTHING = 0;

    private static final int PARSE_NODES = 1;

    private static final int PARSE_EDGES = 2;

    private int state = PARSE_NOTHING;

    /**
     * The network Topological Graph.
     */
    private TopologicalGraph graph = null;

    @Override
    public TopologicalGraph readGraphFile(String filename) throws IOException {

        graph = new TopologicalGraph();

        // lets read the file
        FileReader fr = new FileReader(filename);
        BufferedReader br = new BufferedReader(fr);

        String lineSep = System.getProperty("line.separator");
        String nextLine = null;
        StringBuffer sb = new StringBuffer();

        while ((nextLine = br.readLine()) != null) {
            sb.append(nextLine);
            //
            // note:
            // BufferedReader strips the EOL character.
            //
            sb.append(lineSep);

            // functionality to diferentiate between all the parsing-states
            // state that should just find the start of node-declaration
            if (state == PARSE_NOTHING) {
                if (nextLine.contains("Nodes:")) {
                    // Log.printLine("found start of Nodes... switch to parse nodes!");
                    state = PARSE_NODES;
                }
            }

            // the state to retrieve all node-information
            else if (state == PARSE_NODES) {
                // perform the parsing of this node-line
                parseNodeString(nextLine);
            }

            // the state to retrieve all edges-information
            else if (state == PARSE_EDGES) {
                parseEdgesString(nextLine);
            }

        }

        br.close();

        // Log.printLine("read file successfully...");
        // Log.printLine(sb.toString());

        return graph;
    }

    /**
     * Parses nodes inside a line from the BRITE file.
     *
     * @param nodeLine A line read from the file
     */
    private void parseNodeString(String nodeLine) {
        StringTokenizer tokenizer = new StringTokenizer(nodeLine);

        // number of node parameters to parse (counts at linestart)
        int parameters = 3;

        // first test to step to the next parsing-state (edges)
        if (nodeLine.contains("Edges:")) {
            // Log.printLine("found start of Edges... switch to parse edges!");
            state = PARSE_EDGES;

            return;
        }

        // test against an empty line
        if (!tokenizer.hasMoreElements()) {
            // Log.printLine("this line contains no tokens...");
            return;
        }

        // parse this string-line to read all node-parameters
        // NodeID, xpos, ypos, indegree, outdegree, ASid, type(router/AS)

        int nodeID = 0;
        String nodeLabel = "";
        int xPos = 0;
        int yPos = 0;

        for (int actualParam = 0; tokenizer.hasMoreElements() && actualParam < parameters; actualParam++) {
            String token = tokenizer.nextToken();
            switch (actualParam) {
                case 0:    // Log.printLine("nodeID: "+token);
                    // Log.printLine("nodeLabel: "+token);
                    nodeID = Integer.valueOf(token);
                    nodeLabel = Integer.toString(nodeID);
                    break;

                case 1:    // Log.printLine("x-Pos: "+token);
                    xPos = Integer.valueOf(token);
                    break;

                case 2:    // Log.printLine("y-Pos: "+token);
                    yPos = Integer.valueOf(token);
                    break;
            }
        }

        // instanciate an new node-object with previous parsed parameters
        TopologicalNode topoNode = new TopologicalNode(nodeID, nodeLabel, xPos, yPos);
        graph.addNode(topoNode);

    }

    /**
     * Parses edges inside a line from the BRITE file.
     *
     * @param nodeLine A line read from the file
     */
    private void parseEdgesString(String nodeLine) {
        StringTokenizer tokenizer = new StringTokenizer(nodeLine);

        // number of node parameters to parse (counts at linestart)
        int parameters = 6;

        // test against an empty line
        if (!tokenizer.hasMoreElements()) {
            // Log.printLine("this line contains no tokens...");
            return;
        }

        // parse this string-line to read all node-parameters
        // EdgeID, fromNode, toNode, euclideanLength, linkDelay, linkBandwith, AS_from, AS_to, type

        // int edgeID = 0;
        int fromNode = 0;
        int toNode = 0;
        // float euclideanLength = 0;
        float linkDelay = 0;
        int linkBandwith = 0;

        for (int actualParam = 0; tokenizer.hasMoreElements() && actualParam < parameters; actualParam++) {
            String token = tokenizer.nextToken();
            switch (actualParam) {
                case 0:    // Log.printLine("edgeID: "+token);
                    // edgeID = Integer.valueOf(token);
                    break;

                case 1:    // Log.printLine("fromNode: "+token);
                    fromNode = Integer.valueOf(token);
                    break;

                case 2:    // Log.printLine("toNode: "+token);
                    toNode = Integer.valueOf(token);
                    break;

                case 3:    // Log.printLine("euclideanLength: "+token);
                    // euclideanLength = Float.valueOf(token);
                    break;

                case 4:    // Log.printLine("linkDelay: "+token);
                    linkDelay = Float.valueOf(token);
                    break;

                case 5:    // Log.printLine("linkBandwith: "+token);
                    linkBandwith = Float.valueOf(token).intValue();
                    break;
            }// switch-END
        }// for-END

        graph.addLink(new TopologicalLink(fromNode, toNode, linkDelay, linkBandwith));

    }

}
